#!/usr/bin/python
# -*-coding:Latin-1 -*

#-----------------------------------------
# Carte.py is a part of mahjong-2d project.
# Name:         Carte.py
# Author:       Antoine Morillon
# Copyright:    (C) 2013 Antoine Morillon, Matthieu Morillon
# Licence:      GNU General Public Licence version 3
# Website:      
# Email:        
#-----------------------------------------

import random

g_checkmate = 19 #xxx, sert à quoi ça??

class Carte:
    """Contient les données liees a la carte en cours de jeu"""
    taille = [6,16]
    pas_score = 10 #le pas du score
    temps = 150#tps max de la 1ere partie,décrémenté au fil des parties
    PaireDisponible = [[0,0], [0,0]] #[[X1,Y1],[X2,Y2]] paire tuile dispo
    existePaireDisponible = True
    tuiles = list(xrange(96)) #liste chainée des tuiles (demandée par Interface?) xxx
    tuilesEstActive = None #matrice d'activité des tuiles
    tuilesM = None#matrice de numero de tuiles
    element_cliques = [[0,0],[0,0]] ##si on clique 3 elements
    nb_clique = 0#nombre de tuiles cliquées, vaut 0 ou 1
    est_checkmate = False
    pause = True
    matrice_quadruplets_et_plus_chiants_a_gerer = None
    est_terminee = False #attribut pour savoir si la carte est finie(timer ou plus de paire dispo)

    def __init__(self):
        '''
        Initialisation de l'instance de la classe Carte
        '''
        #mélange l'ordre des tuiles
        random.shuffle(self.tuiles)
        #initialisation des différentes matrices, paires
        self.init_estActive()
        self.tuilesM = self.update_matrice_tuile(self.tuiles)
        self.init_matrice_quadruplet()
        self.PaireDisponible = self.trouver_paire()
                
    def set_ptr_partie(self,ptr_partie):
        '''
        Stockage d'un pointeur vers l'instance de la partie
        '''
        self.partie = ptr_partie

    def set_ptr_main(self, ptr_main):
        '''
        Stockage d'un pointeur vers le main
        '''
        self.ptr_main = ptr_main

    def pause_timer(self):
        '''
        Mise en pause du jeu, via un unique attribut de classe
        '''
        self.pause = True

    def fin_pause_timer(self):
        '''
        Sortie de pause du jeu
        '''
        self.pause = False

    def init_matrice_quadruplet(self):
        '''
        Création de la matrice pour connaitre l'association des paires 
        non triviales. Dans le cas général, la tuile N (avec N paire) va 
        avec la tuile N+1 (donc N+1 impaire).
        Dans certains cas, les tuiles peuvent avoir plusieurs associées 
        (par groupes), ici on garde les valeurs qui vont bien dans un groupe,
        pour la plus petite du groupe.
        '''
        matrice = [ [0] for i in range(0,96)]
        #valeur qui vont bien en dur, surtout pour la plus petite
        matrice[0]=[1,2,3,4,5,6,7]#saisons
        matrice[8]=[9,10,11,12,13,14,15]#fleurs
        matrice[16]=[17,18,19]#lambda
        matrice[38]=[39,40,41]#4-vert
        matrice[60]=[61,62,63]#4-violets
        matrice[70]=[71,72,73]#9-bleus
        self.matrice_quadruplets_et_plus_chiants_a_gerer = matrice

    def init_estActive(self):
        '''
        Initialise la matrice d'activité des tuiles
        active => on la verra, elle sera cliquable dans les moments de jeu
        '''
        estActive = [[True for j in range(0,16)] for i in range (0,6)]
        self.tuilesEstActive = estActive

    def calculCheckmate(self):
        '''
        Vérifie l'existence de tuiles sur la carte quand il n'y a plus de 
        tuiles à relier
        MaJ de l'attribut associé
        '''
        if(self.existePaireDisponible):
            self.est_checkmate = False
            return False
        else:
            #si il existe une tuile active alors checkmate
            for i in range(0,6):
                for j in range(0,16):
                    if(self.tuilesEstActive[i][j]):
                        self.est_checkmate = True
                        return True
            self.est_checkmate = False
            return False
                
    def estActive(self,i,j):
        '''
        Verification de la disponibilité d'une tuile, ou d'une coordonnée
        pouvant sortir du cadre des tuiles (cf algorithme de vérification de 
        disponibilité des tuiles)
        '''
        if(i>=0 and i<6 and j>=0 and j<16):
            return self.tuilesEstActive[i][j]
        else:
            #on est en dehors du cadre, il n'y a pas de tuile actives!
            return False

    ##Amélioration: gérer les fleurs qui vont toutes ensemble
    def memes_tuiles(self,i1,j1,i2,j2):
        '''
        Vérifie que les 2 forment bien une paire, ou appartiennent bien au 
        meme groupe
        '''
        a = self.tuilesM[i1][j1]
        b = self.tuilesM[i2][j2]
        mini = min(a,b)
        maxi = max(a,b)
        
        debut = -1
        if(mini>=0 and mini<=7):
            debut = 0
        elif(mini>=8 and mini<=15):
            debut = 8
        elif(mini>=16 and mini<=19):
            debut = 16
        elif(mini>=38 and mini<=41):
            debut = 38
        elif(mini>=60 and mini<=63):
            debut = 60
        elif(mini>=70 and mini<=73):
            debut = 70
            

        #Pour que tuiles se suivent et la plus petite est paire: (majorité cas)
        if((mini%2==0) and (mini+1==maxi)):
            return True
        #minorité des cas, quadruplets qui font chier
        elif(debut != -1):
            liste = self.matrice_quadruplets_et_plus_chiants_a_gerer[debut]
            longueur = len(liste)
            
            #pour le cas du 0, la longueur est de 7 (1,2,3,...,7)
            #donc len(liste)=7

            #print "on est dans le elif k va aller de 0 à %d"%(longueur)
            #on vérifie que la plus grande est aussi dans la liste
            for k in liste:
                if(maxi == k):
                    #print "memes_tuiles quadruplet reconnu"#debug
                    return True
        
        return False


    def trouver_paire(self):
        '''
        trouve 2 tuiles actives, qui vont ensemble (memes_tuiles) et de 
        façon optimisée (n(n=1)/2)
        mets à jour l'attribut concernant l'existence d'une telle paire
        retourne une telle paire
        '''

        #Methode optimisée:
        #trouve les cases k,k+1
        for k in range(0,96,2):

            (i1,j1) = self.trouveParmisMatrice(k)
            (i2,j2) = self.trouveParmisMatrice(k+1)
            available1 = self.estActive(i1,j1)
            available2 = self.estActive(i2,j2)
            if(available1 and available2):
                result = self.existe_chemin(i1,j1,i2,j2)
                if(result[0]):
                    X1 = [i1,j1]
                    X2 = [i2,j2]
                    X = [X1,X2]
                    self.existePaireDisponible = True
                    return X

        #trouve les paires parmis les quadruplets et autres bizarreries
        for k in range(0,96):
            liste = self.matrice_quadruplets_et_plus_chiants_a_gerer[k]
            liste.insert(0,k)
            longueur = len(liste)
            if(longueur != 1):
                for deb in liste:
                    toto = liste[longueur-1]#-1 pour eviter buffer overflow
                    #print "toto = %s"%toto
                    #debug ici ffs
                    for fin in xrange(deb, toto+1):#+1 pour eviter de s'arreter avant un nombre qu'on veut atteindre!!!
                        if(deb!=fin):
                            #print "deb = %d, fin = %d" %(deb,fin)
                            (i1,j1) = self.trouveParmisMatrice(deb)
                            (i2,j2) = self.trouveParmisMatrice(fin)
                            available1 = self.estActive(i1,j1)
                            available2 = self.estActive(i2,j2)
                            if(available1 and available2):
                                result = self.existe_chemin(i1,j1,i2,j2)
                                if(result[0]):
                                    X1 = [i1,j1]
                                    X2 = [i2,j2]
                                    X = [X1,X2]
                                    #debug
                                    #print "trouver_paire: quadruplet trouvé numéros %d %d, positions: %d,%d; %d,%d"%(deb,fin,i1,j1,i2,j2)
                                    self.existePaireDisponible = True
                                    return X

#DEBUG: INSERTION METHODE BOURRI DANS LA METHODE OPTIMISEE
        #méthode bourrin, xxx, todo, modifier ça, c'est horrible
        # for i in range(0,96):
        #     for j in range(0,96):
        #         if(i != j):
        #             (i1,j1) = self.trouveParmisMatrice(i)
        #             (i2,j2) = self.trouveParmisMatrice(j)
        #             available1 = self.estActive(i1,j1)
        #             available2 = self.estActive(i2,j2)
        #             meme = self.memes_tuiles(i1,j1,i2,j2)
        #             if(available1 and available2 and meme):
        #                 result = self.existe_chemin(i1,j1,i2,j2)
        #                 if(result[0]):
        #                     X1 = [i1,j1]
        #                     X2 = [i2,j2]
        #                     X = [X1,X2]
        #                     print "trouvé paire méthode bourrin, quadruplet trouvé numéros %d %d, positions: %d,%d; %d,%d"%(i,j,i1,j1,i2,j2)
        #                     self.existePaireDisponible = True
        #                     return X
#FIN DEBUG


        #cas ou on ne trouve rien...
        X1 = [0,0]
        X2 = [0,0]
        X = [X1,X2]
        self.existePaireDisponible = False
        #MaJ de l'attribut concernant un possible checkmate
        self.calculCheckmate()
        
        print "Trouver_paire: Pas de paire disponible, carte terminee"#debug
        self.est_terminee = True
        return X
    
    def trouveParmisMatrice(self,k):
        for i in range(0,6):
            for j in range(0,16):
                num = self.tuilesM[i][j]
                if( num == k):
                    return (i,j)        
        return (0,0)

#parcours vertical ou horizontal 1, sont appropriés pour verti,hori,verti

    #i ligne ou est la barre mobile, i1(peut-être aussi i2) la ligne d'une case cliquée,j1(peut-être j2), colonne case cliquée
    def parcoursVertical1(self,i,i1,j1):
        libre = True
        if(i1<i):
            debut=i1
            fin=i
        else:
            debut=i
            fin=i1
        #Check vertical line    
        for k in range(debut,fin+1):
        #On evite de vérifier la valeur de la case de départ
            if(k!=i1):
                #si sur le chemin on rencontre une case encore active on arrête
                if(self.estActive(k,j1)):
                    libre = False
        return libre

    def parcoursHorizontal1(self,i,j1,j2):
        libre = True
        #Check ligne horizontale, on a déjà vérifié l'élément au bord gauche ou droit!
        if(j1<j2):
            debut = j1
            fin = j2
        else:
            debut = j2
            fin = j1
        for k in range(debut, fin+1):
        #evite de regarder 2 fois les éléments aux bords
            if(k!= j1 and k!= j2):
                if(self.estActive(i,k)):
                    libre = False
        return libre

#parcours 2, pour hori,verti,hori
    def parcoursHorizontal2(self,i1,j,j1):
        libre = True
        #Check ligne horizontale
        if(j<j1):
            debut = j
            fin = j1
        else:
            debut = j1
            fin = j
        for k in range(debut, fin+1):
        #evite de regarder les cases cliquees
            if(k!= j1):
                if(self.estActive(i1,k)):
                    libre = False
        return libre

    def parcoursVertical2(self,i1,i2,j):
        libre = True
        if(i1<i2):
            debut=i1
            fin=i2
        else:
            debut=i2
            fin=i1
        #Check vertical line    
        for k in range(debut,fin+1):
        #On evite de vérifier la valeur de la case de départ
            if(k!=i1 and k!=i2):
                #si sur le chemin on rencontre une case encore active on arrête
                if(self.estActive(k,j)):
                    libre = False
        return libre


    def existe_chemin(self,i1,j1,i2,j2):
                '''
                Regarde si il existe un chemin sans tuile
                On ne vérifie pas ici que les tuiles vont ensemble
                Nota aux dev: anciennement appelée ici "verifier_paire"
                '''

                #Fixe la barre à chaque ligne
                #la barre peut sortir du tapis, d'ou -1 et 6 inclus
                for i in range(-1,7):
                    libre = True
                    #vertical
                    libre = self.parcoursVertical1(i,i1,j1)
                    #horizontal
                    if(libre):
                        libre = self.parcoursHorizontal1(i,j1,j2)
                        #vertical
                        if(libre):
                            libre = self.parcoursVertical1(i,i2,j2)
                    if(libre):
                        return (True, i, "Horizontal")

                #Fixe la barre à chaque colonne
                for j in range(-1,17):
                    libre = True
                    #horizontal
                    libre = self.parcoursHorizontal2(i1,j,j1)
                    #vertical
                    if(libre):
                        libre = self.parcoursVertical2(i1,i2,j)
                        #horizontal
                        if(libre):
                            libre = self.parcoursHorizontal2(i2,j,j2)

                    if(libre):
                        return (True, j, "Vertical")
                return (False, 0, "Tout")

    def cliquer(self,i,j):
        '''
        cliquer un element, si il y a déjà une tuile de cliquée,
        vérifie que ce n'est pas la même et clique la 2e tuile, puis
        lance la vérification d'existence de chemin et de compatibilité des 
        tuiles, si elle est bonne, supprime les 2
        elements cliqués, sinon elle ne garde que le dernier
        '''
        
        #On ne peut que cliquer des éléments actifs!
        if(not self.tuilesEstActive[i][j]):
            print "tu cliques des éléments déjà supprimés"
            return -1

        #et avant que le timer soit à 0
        if(self.ptr_main.timer <= 0):
            print "timer out! impossible de cliquer"
            #debug
            if(not self.existePaireDisponible):
                print "en plus tu as déjà gagné, ces tuiles sont buggées"
            return -1

        #et avant que la partie soit terminée
        if(not self.existePaireDisponible):
            #debug:
            print "c'est fini, rien ne sert de cliquer, plus rien de disponible"
            return -1

        #print "on a cliqué %d %d" % (i,j)
        k=0
        if(self.nb_clique == 0):
            self.nb_clique = 1            
        elif(self.nb_clique == 1):
            #Si il y a déjà une tuile de cliquée, on vérifie que ce
            #n'est pas exactement la meme avant de cliquer, sinon on
            #réécrie sur a l'emplacement du 1er element clique
            diff_i = (self.element_cliques[0][0] != i)
            diff_j = (self.element_cliques[0][1] != j)
            if( diff_i or diff_j):
                k=1
                self.nb_clique = 2
                
        self.element_cliques[k][0] = i
        self.element_cliques[k][1] = j
        if(self.nb_clique == 2):
            [[a,b],[c,d]] = self.element_cliques
            
            #si on a cliqué 2 elements différents, le 2e remplace le premier
            if(not self.memes_tuiles(a,b,c,d)):
                self.nb_clique = 1
                self.element_cliques = [[c,d],[0,0]]
                print "Le 2 remplace le 1er"
            #si on a cliqué 2 elements similaires, on vérifie
            else:
                result = self.existe_chemin(a,b,c,d)
                #print "resultat du 2e clique %r" % (result[0])
                #si les tuiles sont joignables, on les vire et MaJ variables
                if(result[0]):
                    self.tuilesEstActive[a][b]= False
                    self.tuilesEstActive[c][d]= False
                    self.nb_clique = 0
                    self.element_cliques = [[0,0],[0,0]]
                    self.partie.score = self.partie.score + self.pas_score
                    self.PaireDisponible = self.trouver_paire() 
                    #print "elements supprimés"
                    
                    return 0
                #sinon les tuiles sont pas joignables, on vire la 1ere et MaJ
                else:
                    self.element_cliques = [[c,d],[0,0]]
                    self.nb_clique = 1
                    print "elements pas supprimés"
                    return 1          

    def update_matrice_tuile(self,tuiles):
        '''
        créer une matrice de numéro des tuiles depuis la liste chainée 
        (mélangée) de tuiles
        '''
        tuilesM = [[0 for j in range(0,16)] for i in range(0,6)] #matrice de la liste d'avant
        l=0;
        for i in tuiles:
            a = 0
            b = 0
            a = l/16
            if l>=16:
                b = l-a*16
            else:
                b = l

            tuilesM[a][b] = i;
        
            l=l+1
        return tuilesM





    

